#if _MSC_VER
 #pragma once
#endif // _MSC_VER

#ifndef __HERZELEID_BASICAUDIOENGINE_INCLUDED__
 #define __HERZELEID_BASICAUDIOENGINE_INCLUDED__
 #include "HerzeleidBasicSoundEffect.hxx"

//////////////////////////////////////////////////////////////////////////////////////////////
HerzeleidMediaNamespaceBegin
	struct AudioEngineBase; TinyTypeDecl( AudioEngineBase );
HerzeleidMediaNamespaceEnd;
//////////////////////////////////////////////////////////////////////////////////////////////
HerzeleidMediaNamespaceBegin class AudioEngineCallbacks : 
	public CoreAudioEngineCallback
{
	AudioEngineBasePtr m_HostPtr;

public:
	AudioEngineCallbacks( );
	Void Initialize( _In_ AudioEngineBasePtr HostPtr );

    // Called by XAudio2 just before an audio processing pass begins.
	Void HERZELEID_METHODSTDCALLTYPE OnProcessingPassStart( );
    // Called just after an audio processing pass ends.
	Void HERZELEID_METHODSTDCALLTYPE OnProcessingPassEnd( );
	// Called when a critical system error causes XAudio2 to be closed and restarted. 
	Void HERZELEID_METHODSTDCALLTYPE OnCriticalError( _In_ HResult Error );

} HerzeleidMediaNamespaceEnd;
//////////////////////////////////////////////////////////////////////////////////////////////
// Represents a very basic functionality for implementing a game audio engine using MF, XAPO and XAudio.
// TODO: Implement Frame() function to rebuild engine in case if critical error occured. 
//////////////////////////////////////////////////////////////////////////////////////////////
HerzeleidMediaNamespaceBegin struct AudioEngineBase
{
	// Internal types 

	typedef XAUDIO2FX_REVERB_PARAMETERS AudioEffectReverbDesc; TinyTypeDecl(AudioEffectReverbDesc);
	typedef IXAudio2MasteringVoice MasteringVoice; TinyTypeDecl(MasteringVoice);
	typedef IXAudio2SubmixVoice SubmixVoice; TinyTypeDecl(SubmixVoice);
	typedef IMFMediaEngine CoreMediaEngine; TinyTypeDecl(CoreMediaEngine);
	typedef IXAudio2 CoreAudioEngine; TinyTypeDecl(CoreAudioEngine);

	// Default the mastering voice to 2 channels to simplify the reverb logic. 
	static UInt32Const DefaultNumInputChannels = 2u;
	// Default the mastering voice to 2 channels to simplify the reverb logic. 
	static UInt32Const DefaultNumOutputChannels = 2u;
	// Default sample rate used within engine base functions. 
	static UInt32Const DefaultSampleRate = 48000u;

protected:

	// Core engines 
	
	// An entirely new engine instance and mastering voice will be created in order to tag sound 
	// effects with the audio "game effects" (AudioCategory_GameEffects) category. 

	WRL::ComPtr<CoreMediaEngine> m_MediaEngine;			// Core media engine. 
	WRL::ComPtr<CoreAudioEngine> m_SoundEffectEngine;	// Core audio engine. 
	AudioEngineCallbacks m_Callbacks;					// Core audio engine callbacks. 

	// Sound effect confgiurations and voices 

	// Create separate engine and mastering voice for sound effects.
	// Game will use many voices mixing all the effect down to a single mastering voice. 

	MasteringVoicePtr m_SoundEffectMasteringVoice; 
	SubmixVoicePtr m_SoundEffectReverbVoiceSmallRoom;
	SubmixVoicePtr m_SoundEffectReverbVoiceLargeRoom;
	AudioEffectReverbDesc m_ReverbParameteresSmallRoom;
	AudioEffectReverbDesc m_ReverbParameteresLargeRoom;

	// Notifiers 

	// This flag indicates that the audio system is experiencing critical errors. 
	Bool m_bEngineExperiencedCriticalError; 
	// This flag indicates whether the engine base was intialized
	Bool m_bEngineInitialized;
	// This flag indicates whether the audio is playing. 
	Bool m_bAudioStarted;

	Void CreateReverb(
		_In_ AudioEffectReverbDescPtr pParams,
		_Inout_ SubmixVoiceAddress pSubmix,
		_In_ Bool bEnableEffect
		);

public:

	// Enabled late diffusion + increased late diffusion (15)
	static HERZELEID_NOALIAS Void GetDefaultReverbConfigurationForSmallRoom( 
		_In_ AudioEffectReverbDescPtr pReverbDesc 
		);
	// Disabled late field + decreased late diffusion (4) + lower density (10)
	static HERZELEID_NOALIAS Void GetDefaultReverbConfigurationForLargeRoom( 
		_In_ AudioEffectReverbDescPtr pReverbDesc 
		);

public:

	Void SetExperiencedCriticalError();
	Bool HasExperiencedCriticalError();

public:

	// Restarts the audio streams. 
	// NOTE: A call to this method must match a previous call to SuspendAudio. 
	// This method causes audio to continue where it left off.
	// NOTE: If there is a problem with the restart, the m_bEngineExperiencedCriticalError
	// flag is set. The next call to Render will recreate all the resources and
	// reset the audio pipeline.
	Void ResumeAudio();

	// Stops all audio immediately.
	// NOTE: It leaves the audio graph untouched, which preserves all effect parameters
	// and effect histories (like reverb effects, voice states, pending buffers,
	// cursor positions and so on).
	// NOTE: When the engines are restarted, the resulting audio will sound as if it had
	// never been stopped except for the period of silence.
	Void SuspendAudio();

public:

	Void CreateResources();
	Void ReleaseResources();

public:

	// Loop count = 0, default volume. 
	// NOTE: For long-time effects its better have pitch shifting + low-pass filter. 
	// (with XAUDIO2_VOICE_USEFILTER and default pitch = 2)
	Void CreateSourceVoice( 
		_Inout_ BasicSoundEffectPtr pEffect,
		_In_ String16ConstRef wszWav
		);

	// Use this function to destroy source voice and source buffer data. 
	Void ReleaseSourceVoice( 
		_Inout_ BasicSoundEffectPtr pEffect
		);

public:

	AudioEngineBase();
	Void Initialize();
	virtual ~AudioEngineBase();

public:

	Void Start();

	// Only of one-off voices.  
	// HERE: Submits the audio buffer only if there is none queued up. 
	Void StartSoundEffect( _In_ BasicSoundEffectPtr pEffect );
	Void StopSoundEffect( _In_ BasicSoundEffectPtr pEffect );
	Bool HasSoundEffectStarted( _In_ BasicSoundEffectPtr pEffect );

	Void SetSoundEffectVolume( _Inout_ BasicSoundEffectPtr pEffect, _In_ Float32 fVolume );
	Void SetSoundEffectPitch( _Inout_ BasicSoundEffectPtr pEffect, _In_ Float32 fPitch );
	Void SetSoundEffectFilter( _Inout_ BasicSoundEffectPtr pEffect, _In_ Float32 fFrequency, _In_ Float32 fOneOverQ );

	Void SetRoomSize( _In_ Float32 fRoomSize, _In_ Float32Ptr pfWallDistances );

} HerzeleidMediaNamespaceEnd;
//////////////////////////////////////////////////////////////////////////////////////////////

#endif // !__HERZELEID_BASICAUDIOENGINE_INCLUDED__